查看原文
其他

WOW怀旧服 明文发包获取和HOOK

Kaining 看雪学苑 2022-07-01
本文为看雪论坛优秀文章
看雪论坛作者ID:Kaining


只用于学习交流,不要用于任何非法用途。如果哪里有不对的地方,请提出来。本人也是刚开始接触,希望能借此多多和大佬们学习学习。


1


找出什么通过什么函数发包 send sendto WSASend


可以看到在send下段断下,其余不断。


2


判断是否为线程发包


通过不同动作,下段查看堆栈是否相同。
喊话 断下地址 返回到 返回自 大小 注释 方 000000000012D808 000000014160FF9D 000007FEFF348000 C0 ws2_32.000007FEFF348000 用户模块000000000012D8C8 0000000141610394 000000014160FF9D 50 wowclassic.000000014160FF9D 用户模块000000000012D918 0000000141627E5F 0000000141610394 140 wowclassic.0000000141610394 用户模块000000000012DA58 0000000141610759 0000000141627E5F 30 wowclassic.0000000141627E5F 用户模块000000000012DA88 0000000140A3561B 0000000141610759 90 wowclassic.0000000141610759 用户模块000000000012DB18 0000000140A356E0 0000000140A3561B 50 wowclassic.0000000140A3561B 用户模块000000000012DB68 0000000140F4EF7E 0000000140A356E0 510 wowclassic.0000000140A356E0 用户模块 000000000012E078 0000000140F3147B 0000000140F4EF7E 1100 wowclassic.0000000140F4EF7E 用户模块000000000012F178 0000000141996779 0000000140F3147B 40 wowclassic.0000000140F3147B 用户模块000000000012F1B8 000000014199A063 0000000141996779 130 wowclassic.0000000141996779 用户模块000000000012F2E8 00000001419960AF 000000014199A063 40 wowclassic.000000014199A063 用户模块000000000012F328 0000000141989707 00000001419960AF 30 wowclassic.00000001419960AF 用户模块000000000012F358 0000000141996D03 0000000141989707 150 wowclassic.0000000141989707 用户模块000000000012F4A8 000000014199627E 0000000141996D03 50 wowclassic.0000000141996D03 用户模块000000000012F4F8 000000014198AB74 000000014199627E 50 wowclassic.000000014199627E 用户模块000000000012F548 00000001403A8E6C 000000014198AB74 70 wowclassic.000000014198AB74 用户模块000000000012F5B8 00000001402ED8C2 00000001403A8E6C 60 wowclassic.00000001403A8E6C 用户模块000000000012F618 0000000141CD873E 00000001402ED8C2 70 wowclassic.00000001402ED8C2 用户模块000000000012F688 00000001402E8763 0000000141CD873E F0 wowclassic.0000000141CD873E 用户模块000000000012F778 00000001402C3C06 00000001402E8763 60 wowclassic.00000001402E8763 用户模块000000000012F7D8 00000001402C3677 00000001402C3C06 90 wowclassic.00000001402C3C06 用户模块000000000012F868 0000000140A3E840 00000001402C3677 40 wowclassic.00000001402C3677 用户模块000000000012F8A8 0000000140A3E3F5 0000000140A3E840 50 wowclassic.0000000140A3E840 用户模块000000000012F8F8 0000000140A3F79A 0000000140A3E3F5 C0 wowclassic.0000000140A3E3F5 用户模块000000000012F9B8 0000000140A3F16F 0000000140A3F79A 30 wowclassic.0000000140A3F79A 用户模块000000000012F9E8 000000014046E7FA 0000000140A3F16F 200 wowclassic.0000000140A3F16F 用户模块000000000012FBE8 0000000077569BD1 000000014046E7FA C0 wowclassic.000000014046E7FA 系统模块000000000012FCA8 00000000775698DA 0000000077569BD1 80 user32.0000000077569BD1 系统模块000000000012FD28 0000000140A3E9F5 00000000775698DA C0 user32.00000000775698DA 用户模块000000000012FDE8 00000001402C2C83 0000000140A3E9F5 50 wowclassic.0000000140A3E9F5 用户模块000000000012FE38 00000001402C26DC 00000001402C2C83 30 wowclassic.00000001402C2C83 用户模块000000000012FE68 000000014019C9AA 00000001402C26DC 50 wowclassic.00000001402C26DC 用户模块000000000012FEB8 00000001401AD6FA 000000014019C9AA 30 wowclassic.000000014019C9AA 用户模块000000000012FEE8 00000001401AD71B 00000001401AD6FA 30 wowclassic.00000001401AD6FA 用户模块000000000012FF18 0000000141AD69D6 00000001401AD71B 40 wowclassic.00000001401AD71B 用户模块000000000012FF58 000000007744652D 0000000141AD69D6 30 wowclassic.0000000141AD69D6 系统模块000000000012FF88 000000007767C521 000000007744652D 50 kernel32.000000007744652D 系统模块000000000012FFD8 0000000000000000 000000007767C521 ntdll.000000007767C521 用户模块
点击登录触发地址 返回到 返回自 大小 注释 方 000000000012ED18 000000014160FF9D 000007FEFE438000 C0 ws2_32.000007FEFE438000 用户模块000000000012EDD8 0000000141610394 000000014160FF9D 50 wowclassic.000000014160FF9D 用户模块000000000012EE28 0000000141627E5F 0000000141610394 140 wowclassic.0000000141610394 用户模块000000000012EF68 0000000141610759 0000000141627E5F 30 wowclassic.0000000141627E5F 用户模块000000000012EF98 0000000140A3561B 0000000141610759 90 wowclassic.0000000141610759 用户模块000000000012F028 0000000140A356E0 0000000140A3561B 50 wowclassic.0000000140A3561B 用户模块000000000012F078 00000001401A54C6 0000000140A356E0 70 wowclassic.0000000140A356E0 用户模块 000000000012F0E8 0000000140405CDF 00000001401A54C6 90 wowclassic.00000001401A54C6 用户模块000000000012F178 000000014042DD2E 0000000140405CDF 30 wowclassic.0000000140405CDF 用户模块000000000012F1A8 0000000141996779 000000014042DD2E 40 wowclassic.000000014042DD2E 用户模块000000000012F1E8 000000014199A063 0000000141996779 130 wowclassic.0000000141996779 用户模块000000000012F318 00000001419960AF 000000014199A063 40 wowclassic.000000014199A063 用户模块000000000012F358 0000000141989707 00000001419960AF 30 wowclassic.00000001419960AF 用户模块000000000012F388 0000000141996D03 0000000141989707 150 wowclassic.0000000141989707 用户模块000000000012F4D8 000000014199627E 0000000141996D03 50 wowclassic.0000000141996D03 用户模块000000000012F528 000000014198AB74 000000014199627E 50 wowclassic.000000014199627E 用户模块000000000012F578 00000001403A8E6C 000000014198AB74 70 wowclassic.000000014198AB74 用户模块000000000012F5E8 0000000141CCE456 00000001403A8E6C 60 wowclassic.00000001403A8E6C 用户模块000000000012F648 0000000141CCF1B8 0000000141CCE456 30 wowclassic.0000000141CCE456 用户模块000000000012F678 0000000141CCF743 0000000141CCF1B8 30 wowclassic.0000000141CCF1B8 用户模块000000000012F6A8 00000001402E93A4 0000000141CCF743 80 wowclassic.0000000141CCF743 用户模块000000000012F728 00000001402C3C06 00000001402E93A4 60 wowclassic.00000001402E93A4 用户模块000000000012F788 00000001402C33CA 00000001402C3C06 60 wowclassic.00000001402C3C06 用户模块000000000012F7E8 00000001402C38AB 00000001402C33CA 90 wowclassic.00000001402C33CA 用户模块000000000012F878 0000000140A3E840 00000001402C38AB 40 wowclassic.00000001402C38AB 用户模块000000000012F8B8 0000000140A3DF97 0000000140A3E840 40 wowclassic.0000000140A3E840 用户模块000000000012F8F8 0000000140A3FB07 0000000140A3DF97 C0 wowclassic.0000000140A3DF97 用户模块000000000012F9B8 0000000140A3F16F 0000000140A3FB07 30 wowclassic.0000000140A3FB07 用户模块000000000012F9E8 000000014046E7FA 0000000140A3F16F 200 wowclassic.0000000140A3F16F 用户模块000000000012FBE8 0000000076F39BD1 000000014046E7FA C0 wowclassic.000000014046E7FA 系统模块000000000012FCA8 0000000076F398DA 0000000076F39BD1 80 user32.0000000076F39BD1 系统模块000000000012FD28 0000000140A3E9F5 0000000076F398DA C0 user32.0000000076F398DA 用户模块000000000012FDE8 00000001402C2C83 0000000140A3E9F5 50 wowclassic.0000000140A3E9F5 用户模块000000000012FE38 00000001402C26DC 00000001402C2C83 30 wowclassic.00000001402C2C83 用户模块000000000012FE68 000000014019C9AA 00000001402C26DC 50 wowclassic.00000001402C26DC 用户模块000000000012FEB8 00000001401AD6FA 000000014019C9AA 30 wowclassic.000000014019C9AA 用户模块000000000012FEE8 00000001401AD71B 00000001401AD6FA 30 wowclassic.00000001401AD6FA 用户模块000000000012FF18 0000000141AD69D6 00000001401AD71B 40 wowclassic.00000001401AD71B 用户模块000000000012FF58 000000007703652D 0000000141AD69D6 30 wowclassic.0000000141AD69D6 系统模块000000000012FF88 000000007716C521 000000007703652D 50 kernel32.000000007703652D 系统模块000000000012FFD8 0000000000000000 000000007716C521 ntdll.000000007716C521 用户模块

我们可以看出明显不是线程发包 140A356E0应该是最外层的发包函数。
这两个应该是最内层的功能函数,我们拿过来,去X64DBG看看。
 
 
发现什么都不断,只有喊话断下。
这里也可以看出,内存里面存放的使我们喊话的内容。
 
进去这个CALL看看,应该就是我们的通用CALL了。
0000000141636FC0 | 48:8BD1 | mov rdx,rcx | rcx:"th"0000000141636FC3 | 48:8B0D A62C3601 | mov rcx,qword ptr ds:[142999C70] | rcx:"th"0000000141636FCA | 48:85C9 | test rcx,rcx | rcx:"th"0000000141636FCD | 74 0B | je wowclassic.141636FDA |0000000141636FCF | 41:B8 02000000 | mov r8d,2 |0000000141636FD5 | E9 96E63FFF | jmp wowclassic.140A35670 |0000000141636FDA | C3 | ret |



3


追出包地址和包长及加密过程


000000014160FF97 FF15 13228D00 call qword ptr ds:[141EE21B0] 发包CALL 0000000014161038F E8 8CFBFFFF call wowclassic.14160FF20 发包CALL 000000000141627E5A E8 61AFFEFF call wowclassic.141612DC0 发包CALL 0000000000141610756 FF50 08 call qword ptr ds:[rax+8] 发包CALL 00000000000140A35616 E8 C5B0BD00 call wowclassic.1416106E0 发包CALL 000000000000140A356DB E8 20FDFFFF call wowclassic.140A35400 发包CALL 0000000000000140BF1E24 E8 9751A400 call wowclassic.141636FC0 发包CALL 0000000


我们从第一个开始看,知道rdx是包地址,r8就是长度,我们都追下。

000000014160FF8E | 45:33C9 | xor r9d,r9d |000000014160FF91 | 44:8BC5 | mov r8d,ebp |000000014160FF94 | 49:8BD6 | mov rdx,r14 | r14 包长 ebp 包长

继续追r14和ebp。
000000014160FF72 | 4C:8B32 | mov r14,qword ptr ds:[rdx] | [rdx] 包长 ebp 包长000000014160FF2B | 8B6A 10 | mov ebp,dword ptr ds:[rdx+10] | [rdx] 包长 [rdx+10] 包长

 
这里被混淆了,我们需要从他上个CALL进来看下,那个是函数头。

我们继续去00那里追rdx。
0000000141610389 | 48:8BD7 | mov rdx,rdi | [rdi] 包长 [rdi+10] 包长0000000141610365 | 48:8BFA | mov rdi,rdx | [rdx] 包长 [rdx+10] 包长0000000141627E54 | 49:8BD7 | mov rdx,r15 | [r15] 包长 [r15+10] 包长0000000141627CD0 | 4C:8D7C24 20 | lea r15,qword ptr ss:[rsp+20] | [rsp+20] 包长 [rsp+20+10] 包长 这里只给了地址 里面是空的

 
这里我们下段发现rsp+20是空的,说明这个只是给他一个地址赋值在下面,我们标注下。
 
我们下段来看下,他是那里给他赋值的,我们方法是,这里下段,看下他的地址里面的值。单步往下走,看什么时候被改变。
 
 
我们单步走下看看,那里改变了这里的值。
 

0000000141627D00 | 41:890408 | mov dword ptr ds:[r8+rcx],eax | 这里给包长赋值


0000000141627D00 | 41:890408 | mov dword ptr ds:[r8+rcx],eax | 这里给包长赋值

我们这个时候可以进去看下是不是:
 
 
看似是一个包,我们继续单步:
 

0000000141627D3B | 8918 | mov dword ptr ds:[rax],ebx | 这里把我们原来的包长给前四个字节

那这里看似就是我们的明文完整包了,下面就是加密了。这里如果我们看不出来,我们可以去通过条件断点,来下一个喊话的发包看看是否我们猜想的这样。
 
 
这里可以看到是我们的明文包。
 
 
经过这里之后给我们加了0x10长度的数据,不知道是什么,后面我们在去看是什么,我们先继续单步。
 
 
这里把原来的包长给了我们的包头的前4个字节,我们继续往下跟。
 
 
经过这里的时候我们发现他的包体被加密了:
0000000141627DA1 | E8 BA754100 | call wowclassic.141A3F360 | 加密包体
 

发现红色包里面被改变了,正好是1B长度,我们可以得到那个1B是要加密的包长。
 

0000000141627DF4 | F241:0F114424 04 | movsd qword ptr ds:[r12+4],xmm0 | 改变包头+4 到 +B位置的这8个字节

继续往下看:
 

0000000141627E4F | 41:894424 0C | mov dword ptr ds:[r12+C],eax | 这里改变了 包头+C 到 +F位置的字节

然后就是我们本来的r15最后send就OK了。这样我们去找下+4到+F分别是来源于什么吧,这C个字节:
 
 
这里我们可以看到,是来源于rbp+10。我们往上看看,那里又可以能是rbp+10。
 
 
他上面有个CALL是取了rbp+10的地址,一般取地址都是要往里面写数据,我们这里下段看看是不是这里往下写的内容。
 
 
目前rbp+10是空,我们过去这个CALL。
 
 
然后给了这些,我们走到发包CALL看看是否是这个值。
 
 
果然 我们包头的4到F这C个字节被这个CALL过后的rbp+10里面的前C个字节给赋值了,这样我们就得到:
0000000141627DE3 | E8 A8824100 | call wowclassic.141A40090 | 生成包头4-F 这13个字节需要的值 存放于rbp+10的前C个字节
  • 首先我们获取到一个明文包内容,但是里面不全部是我们的内容。长度也是加密长度,经过一个CALL后,我们包长+10,这时候这+10字节是我们的包头。

  • 然后给我们的包头前4个字节,我们的加密包长,经过这个加密包长给我们原来的包进行一个CALL里面加密(俗称包体加密CALL)。

  • 然后继续给包头+4到+F位置进行赋值,这个值是来自己上面这个给CALL的 rbp+10里面,因为这个CALL的参数里面有一个是rbp+10的地址。

  • 经过这个CALL之后这里面的值改变了。



4


最内层明文封包CALL分析


我们去到最内层明文发包CALL看下。
 
首先我们要确定参数,我们进去这个CALL看下:
 
 
rcx的值给了别的寄存器。说明rcx用到了:
 
 
rdx也用到了:
 
 
r8好像只用到了低32位。
 
r9是别人赋值来的,看似没有用到。我们先分析rcx rdx r8d这三个吧。
 
 
首先看到rdx是包地址,rdx+0x10存放的是包长。
000000000014D9C0 00000000233A7F80 ..:#....000000000014D9C8 0000000000000000 ........000000000014D9D0 0000001000000012 ........

然后我们看包地址中的包内容:
00000000233A7F80 00050000000737E7 ç7......00000000233A7F88 3837363534333231 1234567800000000233A7F90 0000000000003039 90...... 可以看到 长度 12 就是我们发送的数据结尾 到30我们发的数据就是1234567890 就说明 这个12是总包长 然后我们现在不知道就是 00050000000737E7 这写是什么 我们在发个包看看
000000000014D9C0 0000000021D58320 .Õ!....000000000014D9C8 0000000000000000 ........000000000014D9D0 0000001000000018 ........ 0000000021D58320 00080000000737E7 ç7......0000000021D58328 3131313131313131 111111110000000021D58330 3131313131313131 11111111可以看到 变成了 00080000000737E7 我们基本可以判断 0000000737E7 是相等的 那么为什么是后2字节是0008和0005呢 我们看下第一次 我们发送1234567890 10长度 是5第二次 我们发送1111111111111111 16长度 是8看似是我们长度的一半 我们去构建一个 试试 00060000000737E73131313131313131 31313131发送12个1 看看我们的看看我们的额包内容是不是这个

 
和我们想象的是一样的,那么我们如果发送的长度是13,会是怎样的呢?
000000001FEB1C40 80060000000737E7 ç7......000000001FEB1C48 3131313131313131 11111111000000001FEB1C50 0000003131313131 11111... 发现 其他没什么变换 就是最后一个字节 从 00 变成了80经过我们多次的设计和测试 发现 /2 整除是00 有余是80 也就是我们发送的包长奇数是80 偶数是00

r8d经过分析发现是恒不变的1。rcx后面再去分析,是一个表达式,从下网上推就OK了。
000000014166C7EC | 48:8B8B E0000000 | mov rcx,qword ptr ds:[rbx+E0] | 参数1 [rbx+E0] 000000014166C7AC | 48:8BD9 | mov rbx,rcx | 参数1 [rcx+E0] 0000000140A69000 | 48:8B8CFB 90010000 | mov rcx,qword ptr ds:[rbx+rdi*8+190] | 参数1 [[rbx+rdi*8+0x190]+E0]测试了rdi == 0 0000000140A68E28 | 48:8BD9 | mov rbx,rcx | 参数1 [[rcx+rdi*8+0x190]+E0] 0000000140A690E8 | 48:8BCE | mov rcx,rsi | 参数1 [[rsi+rdi*8+0x190]+E0] 0000000140A690A8 | 48:8BF1 | mov rsi,rcx | 参数1 [[rcx+rdi*8+0x190]+E0] 0000000141693073 | 48:8B0D F63B3601 | mov rcx,qword ptr ds:[1429F6C70] | 参数1 [[[0x00000001429F6C70]+rdi*8+0x190]+E0]0000000141693073 | 48:8B0D F63B3601 | mov rcx,qword ptr ds:[1429F6C70] | 参数1 [[[wowclassic.exe + 0x29F6C70]+rdi*8+0x190]+E0]

通过简单的推算就推出了检测封包,下面我们去VS里面调用一下他。
void CDLG_MAIN::OnBnClickedButton2(){ // TODO: 在此添加控件通知处理程序代码 BYTE b_packet[18] = {0xE7, 0X37, 0X07, 0X00, 0X00, 0X00, 0X05, 0x00, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 }; DWORD dw_temp_array[6] = { 0, 0, 0, 0, 0x12, 0x10 }; *(QWORD*)dw_temp_array = (QWORD)b_packet; QWORD qw_rdx = (QWORD)dw_temp_array; fun_send_call(base_send, qw_rdx, 1);} __declspec(naked) void fun_send_call(QWORD qw_rcx,QWORD qw_rdx,DWORD dw_r8d){ __asm { push rax sub rsp, 0x100 add rcx, base_module_base mov rcx, [rcx] add rcx, offset_send_one mov rcx, [rcx] add rcx, offset_send_two mov rcx, [rcx] mov rax, [rcx] add rax, 0x8 mov rax, [rax] call rax add rsp, 0x100 pop rax retn }}



5


Hook send函数


因为找个游戏有些保护,如果直接修改他的代码是不行的。所以我们只能从send下手,从堆栈找出需要的数据。
 
Hook就不多说了,直接贴代码吧:
void CDLG_MAIN::OnBnClickedButton3(){ // TODO: 在此添加控件通知处理程序代码 fun_hook_send();} void CDLG_MAIN::OnBnClickedButton4(){ // TODO: 在此添加控件通知处理程序代码 fun_reduction_send();} void fun_hook_send(){ g_hook_send = new CInlineHook((__int64)send, (__int64)fun_send_proc,false,15); g_hook_send->MotifyAddress();} void fun_reduction_send(){ g_hook_send->RestoreAddress(); delete g_hook_send; g_hook_send = nullptr;} // __int64 fun_send_proc_jmp = ((__int64)send + 15);void fun_send_proc(){ /* 00007FFDDDC21210 | 48:895C24 08 | mov qword ptr ss:[rsp+8],rbx | [rsp+8]:&"P#F凖\x7F" 00007FFDDDC21215 | 48:896C24 10 | mov qword ptr ss:[rsp+10],rbp | 00007FFDDDC2121A | 48:897424 18 | mov qword ptr ss:[rsp+18],rsi | */ // 这里如果是CALL进来的 rsp要+8 所以后面每个都要+8 __asm { mov qword ptr ss : [rsp + 0x10], rbx mov qword ptr ss : [rsp + 0x18], rbp mov qword ptr ss : [rsp + 0x20], rsi retn }}
HOOK类CInlineHook.h #pragma once#include <windows.h>#include <stdio.h> class CInlineHook{private:#ifndef _WIN64 using uchar = unsigned char; uchar* m_cOriginalByte; //原始OPCODE uchar* m_cMyByte; //自己的OPCODE int m_nOriginalAddress; //原始地址 int m_nMyAddress; //自己的函数地址 int m_nHookLen; DWORD MotifyMemProtect(int nAddress,DWORD dwProtect = PAGE_EXECUTE_READWRITE);#else using uchar = unsigned char; uchar* m_cOriginalByte; //原始OPCODE uchar* m_cMyByte; //自己的OPCODE __int64 m_nOriginalAddress; //原始地址 __int64 m_nMyAddress; //自己的函数地址 int m_nHookLen; DWORD MotifyMemProtect(__int64 nAddress, DWORD dwProtect = PAGE_EXECUTE_READWRITE);#endif public:#ifndef _WIN64 // bPattern true 为jmp false 为call CInlineHook(int nOriginalAddress, int nMyAddress, bool bPattern = true, int nHookLen = 5);#else // bPattern true 为jmp false 为call CInlineHook(__int64 nOriginalAddress, __int64 nMyAddress, bool bPattern = true, int nHookLen = 12);#endif void MotifyAddress(); void RestoreAddress(); ~CInlineHook();}; CInlineHook.cpp #include "pch.h"#include "CInlineHook.h" CInlineHook::~CInlineHook(){ delete[] m_cMyByte; m_cMyByte = nullptr; delete[] m_cOriginalByte; m_cOriginalByte = nullptr;} void CInlineHook::MotifyAddress(){ DWORD dwProtect = MotifyMemProtect(m_nOriginalAddress); memcpy(reinterpret_cast<void*>(m_nOriginalAddress), m_cMyByte, m_nHookLen); MotifyMemProtect(m_nOriginalAddress, dwProtect);} void CInlineHook::RestoreAddress(){ DWORD dwProtect = MotifyMemProtect(m_nOriginalAddress); memcpy(reinterpret_cast<void*>(m_nOriginalAddress), m_cOriginalByte, m_nHookLen); MotifyMemProtect(m_nOriginalAddress, dwProtect);} #ifndef _WIN64 DWORD CInlineHook::MotifyMemProtect(int nAddress, DWORD dwProtect){ DWORD dwOldProtect; VirtualProtect(reinterpret_cast<void*>(nAddress), m_nHookLen, dwProtect, &dwOldProtect); return dwOldProtect;} CInlineHook::CInlineHook(int nOriginalAddress, int nMyAddress, bool bPattern, int nHookLen) :m_nOriginalAddress(nOriginalAddress), m_nMyAddress(nMyAddress), m_nHookLen(nHookLen){ m_cMyByte = new uchar[nHookLen]; m_cOriginalByte = new uchar[nHookLen]; if (bPattern) { m_cMyByte[0] = '\xE9'; } else { m_cMyByte[0] = '\xE8'; } int nOffset = nMyAddress - (nOriginalAddress + 5); memcpy(&m_cMyByte[1], &nOffset, 4); for (int i = 0; i < nHookLen - 5; i++) { m_cMyByte[i + 5] = '\x90'; } DWORD dwProtect = MotifyMemProtect(nOriginalAddress); memcpy(m_cOriginalByte, reinterpret_cast<void*>(nOriginalAddress), m_nHookLen); MotifyMemProtect(nOriginalAddress, dwProtect);} #else DWORD CInlineHook::MotifyMemProtect(__int64 nAddress, DWORD dwProtect /*= PAGE_EXECUTE_READWRITE*/){ DWORD dwOldProtect; VirtualProtect(reinterpret_cast<void*>(nAddress), m_nHookLen, dwProtect, &dwOldProtect); return dwOldProtect;} CInlineHook::CInlineHook(__int64 nOriginalAddress, __int64 nMyAddress, bool bPattern /*= true*/, int nHookLen /*= 12*/) :m_nOriginalAddress(nOriginalAddress), m_nMyAddress(nMyAddress), m_nHookLen(nHookLen){ m_cMyByte = new uchar[nHookLen]; m_cOriginalByte = new uchar[nHookLen]; m_cMyByte[0] = '\x48'; m_cMyByte[1] = '\xB8'; memcpy(&m_cMyByte[2], &nMyAddress,8); m_cMyByte[10] = '\xFF'; if (bPattern) { m_cMyByte[11] = '\xE0'; } else { m_cMyByte[11] = '\xD0'; } if (nHookLen > 12) { for (int i = 12; i < nHookLen; i++) { m_cMyByte[i] = '\x90'; } } DWORD dwProtect = MotifyMemProtect(nOriginalAddress); memcpy(m_cOriginalByte, reinterpret_cast<void*>(nOriginalAddress), m_nHookLen); MotifyMemProtect(nOriginalAddress, dwProtect);} #endif


6


堆栈数据找出类似明文封包


我们最内层明文包是send返回第3层,最外层明文包是send返回第6层,之前我们可以得到,所以我们要找他的数据,我们只要在3-6之前找就OK了。我们来看下:
000000000014D958 000000014166C809 返回到 wowclassic.000000014166C809 自 ???000000000014D960 00000000005688A0 000000000014D968 0000000000000001 000000000014D970 0000000000568AB0 000000000014D978 0000000000000012 000000000014D980 000000000056D680 000000000014D988 0000000140A6902B 返回到 wowclassic.0000000140A6902B 自 wowclassic.000000014166C790000000000014D990 0000000000000000 000000000014D998 000000000056D610 000000000014D9A0 0000000000000000 000000000014D9A8 00000001404A31BD 返回到 wowclassic.00000001404A31BD 自 wowclassic.00000001401C9940000000000014D9B0 000000000014DA00 000000000014D9B8 000000000014DA54 000000000014D9C0 000000002405CC60 000000000014D9C8 0000000000000000 000000000014D9D0 0000001000000012 000000000014D9D8 0000000100000000 000000000014D9E0 0000000000000000 000000000014D9E8 0000000000000000 000000000014D9F0 00000000335B08C0 &"@灚@\x01"000000000014D9F8 0000000000000000 000000000014DA00 0000000000000002 000000000014DA08 000000000056D610 000000000014DA10 0000000141F55DC0 wowclassic.0000000141F55DC0000000000014DA18 0000000140A690F0 返回到 wowclassic.0000000140A690F0 自 wowclassic.0000000140A68E10

我们去看下这些地址:
 
 
可以看出,在rsp+0x320处可以看到类似明文的地方。
 
又因为我们要过滤心跳包,所以这里要看下+0x310处的地址是否为:
 
 
如果不是这个就是心跳包或者别的包,我们不需要,我们只要功能CALL。
 
+0x310是不完整或者太完整的明文包,然后r8是我们的长度。好的,我们知道了这些之后就可以了。
 
 
可以看出,心跳+0x310是个看不懂的东西了,不是返回到,说明心跳包就没有走到这个CALL。
 

喊话断下 也是这里 我们给他记录下0x140A690F0 == wowclassic.exe + 0xA690F0
void fun_send_proc(){ /* 00007FFDDDC21210 | 48:895C24 08 | mov qword ptr ss:[rsp+8],rbx | [rsp+8]:&"P#F凖\x7F" 00007FFDDDC21215 | 48:896C24 10 | mov qword ptr ss:[rsp+10],rbp | 00007FFDDDC2121A | 48:897424 18 | mov qword ptr ss:[rsp+18],rsi | */ // 这里如果是CALL进来的 rsp要+8 所以后面每个都要+8 __asm { push rax ; 这里因为push了 所以 + 0x310变成了 + 0x318 mov rax, [rsp + 0x318] mov hook_send_rsp_cmp, rax ; +0x320里面存放的就是数据 mov rax, [rsp + 0x328] mov get_hook_send_packet, rax mov get_hook_send_len, r8 pop rax } // 这里如果相等就说明是功能明文包 不然就心跳包 直接过滤 get_hook_send_rsp_0x310 = (__int64)base_module_base + base_hook_send_rsp_0x310; if (hook_send_rsp_cmp == get_hook_send_rsp_0x310) { __asm { push rax push rbx push rcx push rdx push rsi push rdi push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15 sub rsp,0x100 } fun_out_packet_text(); __asm { add rsp,0x100 pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop r9 pop r8 pop rdi pop rsi pop rdx pop rcx pop rbx pop rax } } __asm { mov qword ptr ss : [rsp + 0x8], rbx mov qword ptr ss : [rsp + 0x10], rbp mov qword ptr ss : [rsp + 0x18], rsi jmp fun_send_proc_jmp }} void fun_hook_send(){ g_hook_send = new CInlineHook((__int64)send, (__int64)fun_send_proc,true,15); g_hook_send->MotifyAddress();} void fun_reduction_send(){ g_hook_send->RestoreAddress(); delete g_hook_send; g_hook_send = nullptr;} void fun_out_packet_text(){ printf("长度:%d", get_hook_send_len); byte *ptr = 0; char str_temp[0x2000]; char str_out[0x2000] = ""; for (__int64 i = 0; i < get_hook_send_len; i++) { sprintf_s(str_temp, "%02X", *(byte*)(get_hook_send_packet + i)); strcat_s(str_out, str_temp); } printf("\t\t\t明文包内容:%s\r\n", str_out); delete ptr; if (ptr != nullptr) { ptr = nullptr; }}我们这里已经能正确输出所有的功能CALL的函数了

 
可以发现,喊话内容只有一部分是对的,我们需要去修改。

我们继续去看下,我们这个明文包和第外层明文包和最内层明文包的区别,转换成最内层明文包。因为我们需要自己去调用最内层明文包,然后自己加密和发包。



7


分析堆栈包和最内层明文包和最外层明文包的关系
0000000140A690EB | E8 20FDFFFF | call wowclassic.140A68E10 | 最外层明文包000000014166C806 | FF50 08 | call qword ptr ds:[rax+8] | 最内层明文包

+0x320地址的明文包:
000000000014DAE0 0000000142185CE8 è\.B.... "@刅@\x01"000000000014DAE8 8000001200000000 ........000000000014DAF0 0000000000000000 ........000000000014DAF8 0000000000000000 ........000000000014DB00 3131313100000007 ....1111000000000014DB08 0000313131313131 111111..

最外层明文包 rdx+8是包地址 rdx+0x18是包长度:
包地址里面是这样的00000000310D4390 00000000310D3E90 .>.1.... &"怑\r1"00000000310D4398 FFFF000142591BE0 à.YB..ÿÿ00000000310D43A0 00050000000737E7 ç7......00000000310D43A8 3131313131313131 1111111100000000310D43B0 0000000000003131 11......

最内层明文包 rdx 包地址 rdx+0x10是包长度:
0000000021FB6710 00050000000737E7 ç7......0000000021FB6718 3131313131313131 111111110000000021FB6720 0000000000003131 11......

我们可以看出,最外层明文包和最内层明文包多了 16个字节,我们可以砍掉就OK了。那么我们怎么才能通过+0x320的封包,得到最外层的封包呢?
 
 
我们可以看到这里,也就是最外层明文包的这里+8的位置就是我们+0x320的包,因为rsp是不会变的。
 
 
我们看下这个rsp+0x28这里,到底是哪里给他改了。
 

 
那我们来分析下这个CALL就好了。
0000000140A690CA | 48:8B03 | mov rax,qword ptr ds:[rbx] |0000000140A690CD | 48:8D5424 20 | lea rdx,qword ptr ss:[rsp+20] |0000000140A690D2 | 48:8BCB | mov rcx,rbx |0000000140A690D5 | FF50 10 | call qword ptr ds:[rax+10] | 这个CALL 讲内容转换成了 最内层的明文包 可以看出 这个CALL就是只有 两个参数 rcx和rdx 还有一个CALL rax rax来自 rbx rcx来自rbx 也就是 我们只要找处 rbx和rdx即可

 
这里我们可以看出rbx和我们+0x320获取的值是一样的这样我们要看下 rsp+0x20那里是什么。
000000000014DA40 0000000141F55DC0 À]õA.... wowclassic.0000000141F55DC0000000000014DA48 0000000022218F10 ..!"....000000000014DA50 0000010000000000 ........000000000014DA58 FFFFFFFF00000010 ....ÿÿÿÿ000000000014DA60 0000000000000001 ........ 可以看到 +0 就是个 基址 wowclassic.exe + 0x1F55DC0 +8 看似是个申请出来的地址 每次都会变 存放的就是 下面第一张图 +10 是个固定的值0000010000000000 +18 运行CALL 之后被填入长度 +20 就是一个固定的1

 
我们获取+0x320处的代码,转换之后就可以获取到最外层的明文CALL,然后经过减去前0x10个字节,就是完整的明文包了。可以去写下代码。


8


完成转换输出封包内容


 
用代码修改获取下,发现就是正确的明文封包了。
// 明文解析 解析0x320处的明文封包解析成最外层明文封包__declspec(naked) void fun_plaintext_parsing(QWORD qw_rcx, QWORD qw_rdx){ /* 0000000140A690CA | 48:8B03 | mov rax,qword ptr ds:[rbx] | 0000000140A690CD | 48:8D5424 20 | lea rdx,qword ptr ss:[rsp+20] | 0000000140A690D2 | 48:8BCB | mov rcx,rbx | 0000000140A690D5 | FF50 10 | call qword ptr ds:[rax+10] | 这个CALL 讲内容转换成了 最内层的明文包 */ __asm { push rax sub rsp,0x100 mov rax, get_hook_send_packet mov rax, [rax] add rax, 0x10 call[rax] add rsp, 0x100 pop rax retn }} void fun_out_packet_text(){ QWORD qw_rdx; QWORD qw_rcx = get_hook_send_packet; QWORD qw_one = (__int64)base_module_base + base_packet_to_call_stc_address; BYTE* ptr_packet = new BYTE[100]; QWORD qw_stc[5] = { qw_one,(QWORD)ptr_packet,0x0000010000000000,0xFFFFFFFF00000010,1 }; qw_rdx = (QWORD)&qw_stc; //printf("rcx == %I64d", &qw_rcx); //printf("rdx == %I64d", &qw_rdx); fun_plaintext_parsing(qw_rcx, qw_rdx); byte *ptr = 0; char str_temp[0x2000] = ""; char str_out[0x2000] = ""; DWORD dw_len = (DWORD)qw_stc[3]; printf("长度:%d", dw_len - 0x10); QWORD qw_address = qw_stc[1] + 0x10; // 获取存取明文包地址 for (__int64 i = 0; i < dw_len - 0x10; i++) { //printf("地址 == %I64d", qw_address + i); sprintf_s(str_temp, "%02X", *(byte*)(qw_address + i)); strcat_s(str_out, str_temp); } printf("\t\t\t明文包内容:%s\r\n", str_out); delete ptr; delete ptr_packet; if (ptr != nullptr) { ptr = nullptr; } if (ptr_packet != nullptr) { ptr_packet = nullptr; }}


 


看雪ID:Kaining

https://bbs.pediy.com/user-home-846211.htm

*本文由看雪论坛 Kaining 原创,转载请注明来自看雪社区



# 往期推荐

1. X86内核笔记_2_驱动开发

2. 新的漏洞分析体验:CVE-2010-3333 RTF栈缓冲区溢出漏洞

3. 某鹅安全竞赛20年初赛ring0题解

4. 某知笔记服务端docker镜像授权分析

5. angr学习(三)一道自己模仿着出的简单题和angr-ctf符号化输入相关题目

6. 记一次Word宏Downloader样本分析



公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存